using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using XLua;
using Object = UnityEngine.Object;

public class ResMgr : Singleton<ResMgr>
{
    public const string prefix = Const.prefix;
    public Dictionary<string, AssetBundle> loadedBundle = new Dictionary<string, AssetBundle>();

    //ab依赖 资源计数
    private Dictionary<string, int> abRefCountDic = new Dictionary<string, int>(); //每个ab包被引用的次数
    private Dictionary<string, List<string>> abRefDepentDic = new Dictionary<string, List<string>>();  // 每个ab包引用的所有ab包



    private AssetBundleManifest _mainABMani = null;
    private AssetBundleManifest mainABMani
    {
        get {
            if(_mainABMani)
            {
                return _mainABMani;
            }
            AssetBundle mainAB = null;
            if (Application.platform == RuntimePlatform.Android)
            {
                mainAB = AssetBundle.LoadFromFile("file://" + Utils.GetLocalResLoadPath() + "/AssetBundle");
            }
            else
            {
                mainAB = AssetBundle.LoadFromFile(Utils.GetLocalResLoadPath() + "/AssetBundle");
            }
            AssetBundleManifest manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            _mainABMani = manifest;
            return manifest;
        }
    }

    //外部资源加载的路径
    private string OutSideABPath = Const.RES_IP + "AssetBundle/";
    //UI ab资源的加载路径(本地AB包)   
    private string _ABPath = null;
    private string ABPath
    {
        get
        {
            if (_ABPath != null)
            {
                return _ABPath;
            }
            //AssetBundle mainAB = null;
            if (Application.platform == RuntimePlatform.Android)
            {
                _ABPath = "file://" + Utils.GetLocalResLoadPath() + "/";
            }
	        else if (Application.platform == RuntimePlatform.OSXEditor)
            {
                _ABPath = "file:///" + Utils.GetLocalResLoadPath() + "/";
            }
            else
            {
                _ABPath = Utils.GetLocalResLoadPath() + "/";
            }
            return _ABPath;
        }
    }
    //当前加载的配置路径
    private string _currPath;
    private AssetBundle mainAB;

    public void LoadRes(string abName , string resName , LuaFunction function , int level, UnityAction<Object> action = null)
    {
        AddResRequest(abName, resName, function, ABPath, level, action);
    }

    public void LoadSprite(string abName, string resName, LuaFunction function)
    {
        AddResRequest(abName, resName, function, ABPath, 0);
    }

    public void LoadOutSideSprite(string abName, string resName, LuaFunction function)
    {
        AddResRequest(abName, resName, function, OutSideABPath, 0);
    }


    IEnumerator LoadResrator<T>(string abName, string resName , string path) where T:Object
    {
        abName = GetABFullName(abName);
        //Debug.Log("LoadResrator -- > " + _currPath + " -- abName  "+ abName);
        //LoadDependRes(abName);
        if (loadedBundle.ContainsKey(abName))
        {
            AssetBundle ab = loadedBundle[abName];
        }
        else
        {
            //Debug.Log("www Request Resource -- > " + path + abName);
            using (UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(path + abName))
            {
                yield return www.SendWebRequest();
                //最终获得
                AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
                if (www.error != null)
                {
                    Debug.LogError(www.error + ": " + path + abName);
                    EventManager.DispatchEvent("www.error", www.error);
                    fastReqList.Dequeue();
                    yield return null;
                }
                if (ab != null)
                {
                    //缓存起来 已经加载了的AB包
                    loadedBundle[abName] = ab;
                    //AddAbRefCount(abName);
                }
            }
        }
    }

    IEnumerator LoadResAB(string abName)
    {
        LoadDependRes(abName);
        //Debug.Log("加载ab包----> " + abName);
        if (!loadedBundle.ContainsKey(abName))
        {
            using (UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(ABPath + abName))//"file://" + _abPath + "/" + abName))
            {
                yield return www.SendWebRequest();
                //最终获得
                AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
                if (www.error != null)
                {
                    yield return null;
                }
                if (ab != null)
                {
                    loadedBundle[abName] = ab;
                }
            }
        }
        AddAbRefCount(abName);
    }

    public void AddAbRefCount(string abName)
    {
        abName = GetABFullName(abName);
        if (abRefCountDic.ContainsKey(abName))
        {
            abRefCountDic[abName]++;
        }
        else
        {
            abRefCountDic[abName] = 1;
        }
    }
    public void AddAbRefList(string abName,string refAbName)
    {
        abName = GetABFullName(abName);
        refAbName = GetABFullName(refAbName);
        if (abRefDepentDic.ContainsKey(abName))
        {
            var list = abRefDepentDic[abName];
            if (!list.Contains(refAbName))
            {
                abRefDepentDic[abName].Add(refAbName);
            }
        }
        else
        {
            abRefDepentDic[abName] = new List<string>() { refAbName };
        }
    }

    
    /// <summary>
    /// 卸载某个资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="force"></param>
    public void UnloadRes(string abName,bool force = true)
    {
        abName = GetABFullName(abName);
        // Debug.Log(abName + "   资源卸载");
        if (loadedBundle.ContainsKey(abName))
        {
            
            //检查资源的引用计数 将不用的资源也给卸载了
            if (abRefDepentDic.ContainsKey(abName)){
                foreach (var name in abRefDepentDic[abName])
                {
                    if (abRefCountDic.ContainsKey(name) && abRefCountDic[name] == 1)
                    {
                        abRefCountDic[name] = 0;
                        UnloadRes(name);
                    }
                    else if(abRefCountDic.ContainsKey(name) && abRefCountDic[name] > 1)
                    {
                        abRefCountDic[name]--;
                    }
                }

                loadedBundle[abName].Unload(force);
                loadedBundle.Remove(abName);

                abRefDepentDic.Remove(abName);
            }
            else
            {
                if (!abRefCountDic.ContainsKey(abName) || abRefCountDic[abName] == 0)
                {
                    loadedBundle[abName].Unload(force);
                    loadedBundle.Remove(abName);
                }
            }
        }
    }

    //加载某个ab的依赖
    public void LoadDependRes(string abName)
    {
       // Debug.Log("查看AB的依赖" + abName);
        var _name = GetABName(abName);
        string[] depentNames = mainABMani.GetAllDependencies(abName);
        for (int i = 0; i < depentNames.Length; i++)
        {
            AddAbRefList(abName, depentNames[i]);
            //使用新的接口加载ab包
            StartCoroutine(LoadResAB(depentNames[i]));
            //AddResRequest(depentNames[i], null, null, ABPath);
        }
    }

    public Queue<ResRequestVO> reqPool = new Queue<ResRequestVO>();
    public const int MAX_POOL = 10;

    public Queue<ResRequestVO> fastReqList = new Queue<ResRequestVO>();
    public Queue<ResRequestVO> slowReqList = new Queue<ResRequestVO>();

    private float currTime = 0f;

    public void Update()
    {
        if (fastReqList.Count == 0 && slowReqList.Count == 0)
        {
            currTime = Time.time;
            return;
        }

        if (fastReqList.Count > 0)
        {
            CheckQueueResState(fastReqList);
        }

        if(Time.time - currTime >= 0.2f)
        {
            CheckQueueResState(slowReqList);
            currTime = Time.time;
        }
    }

    private void CheckQueueResState(Queue<ResRequestVO> queue)
    {
        if (queue.Count <= 0) return;
        var vo = queue.Peek();
        //是否加载完全部依赖资源了
        if (!vo.isLoading)
        {
            vo.LoadRes();
        }
        else
        {
            if (vo.CheckDependSuccess() && !vo.isLoadingMain)
            {
                vo.LoadMainRes();
            }
            if (vo.CheckLoaded())
            {
                var obj = vo.GetAbRes();
                //有加载特定资源则把obj弹出去
                if (obj)
                {
                    if (vo.function != null)
                    {
                        vo.function.Call(obj);
                    }
                    vo.action?.Invoke(obj);
                }
                //缓存池还没满就把对象重置后放入缓存池
                if(reqPool.Count< MAX_POOL)
                {
                    vo.Reset();
                    reqPool.Enqueue(vo);
                }
                else
                {
                    vo = null;
                }
                queue.Dequeue();
            }
        }
    }

    /// <summary>
    /// 请求加载一个ab资源
    /// </summary>
    /// <param name="abName">ab包名</param>
    /// <param name="resName">资源名 为null则只加载ab包</param>
    /// <param name="function">lua回调</param>
    /// <param name="path">加载路径</param>
    /// <param name="level"></param>
    /// <param name="action"></param>
    public void AddResRequest(string abName , string resName , LuaFunction function , string path , int level = 0 , UnityAction<Object> action = null)
    {
        abName = GetABFullName(abName);
        //print("addRequest ==== >>>>  " + abName + "");
        ResRequestVO vo = null;
        if (reqPool.Count >= MAX_POOL || reqPool.Count == 0)
        {
            vo = new ResRequestVO();
        }
        else
        {
            vo = reqPool.Dequeue();
        }
        vo.abName = abName;
        vo.resName = resName;
        vo.level = level;
        vo.function = function;
        vo.action = action;
        vo.path = path;

        Queue<ResRequestVO> tarList = null;
        if (level == 0)
        {
            //快速队列
            tarList = fastReqList;
            //print("add to fast  "+ abName);
        }
        else
        {
            tarList = slowReqList;
            //慢速队列
        }
        tarList.Enqueue(vo);
    }

    public class ResRequestVO
    {
        public string abName;
        public string resName;
        public int level;

        public LuaFunction function;
        public UnityAction<Object> action;

        public bool isLoading = false;
        public bool isLoadingDepend = false;
        public bool isLoadingMain = false;

        public string[] dependList = new string[] { };
        public string path;

        public void LoadRes()
        {
            isLoading = true;
            if (this.CheckLoaded())
            {
                isLoadingMain = false;
                return;
            }
            //检查是否有依赖
            dependList = ResMgr.Instance.mainABMani.GetAllDependencies(abName);
            if(dependList.Length > 0)
            {
                isLoadingDepend = true;
                ResMgr.Instance.LoadDependRes(abName);
            }
        }

        public void LoadMainRes()
        {
            isLoadingMain = true;
            //直接加载这个资源
            ResMgr.Instance.StarCorLoadRes<Object>(abName, resName , path);
        }

        public bool CheckLoaded()
        {
            //print("111" + CheckDependSuccess());
            //print("222" + ResMgr.Instance.loadedBundle.ContainsKey(abName) + abName);
            return CheckDependSuccess() && ResMgr.Instance.loadedBundle.ContainsKey(abName);
        }

        public Object GetAbRes()
        {
            if (resName == null)
            {
                return null;
            }
            var ab = ResMgr.Instance.loadedBundle[abName];
            return ab.LoadAsset(this.resName);
        }


        //检查依赖资源是否加载完毕
        public bool CheckDependSuccess()
        {
            if (dependList.Length == 0) return true;
            foreach (var item in dependList)
            {
                if (!ResMgr.Instance.loadedBundle.ContainsKey(item))
                {
                    return false;
                }
            }
            return true;
        }

        public void Reset()
        {
           abName = null;
           resName = null;
            path = null;
            level = 0 ;
            
            function = null;
            action = null;
            
            isLoading = false;
            isLoadingDepend = false;
            isLoadingMain = false;
          }
    }


    public void StarCorLoadRes<T>(string abName, string resName , string path) where T : Object
    {
        StartCoroutine(LoadResrator<T>(abName, resName , path));
    }

    public string GetABFullName(string abName)
    {
        if (Utils.FindInString(abName, prefix))
        {
            return abName;
        }
        else
        {
            return abName + prefix;
        }
    }


    public string GetABName(string abName)
    {
        if (!Utils.FindInString(abName, prefix))
        {
            return abName;
        }
        else
        {
            return abName.Replace(prefix,"");
        }
    }
}
